package com.service.impl;

import com.dao.ProductDao;
import com.exception.SeckillException;
import com.github.benmanes.caffeine.cache.Cache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.service.SeckillService;

import javax.annotation.PostConstruct;
import java.time.Duration;

@Service
@Transactional(rollbackFor = RuntimeException.class)
public class SeckillServiceImpl implements SeckillService {

    private static final String PRODUCT_FREFIX = "product:";

    @Autowired
    private ProductDao dao;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private Cache<String, Boolean> localCache;

    /**
     * 缓存预热
     */
    @PostConstruct
    public void initProductCache() {
        dao.listProduct().forEach(product -> {
            //将商品信息加入缓存
            String key = PRODUCT_FREFIX + product.getId();
            redisTemplate.opsForValue().set(key, product.getStock(), Duration.ofMinutes(60));
        });
    }

    /**
     * 在缓存中扣减库存
     * @param id
     */
    @Override
    public void decrProduct(int id) {
        //先判断本地缓存中是否为null，不为null就还表示已经售完，就不需要再访问redis
        if(localCache.getIfPresent(PRODUCT_FREFIX + id) != null) {
            throw new SeckillException("JVM===>商品("+id+")无剩余库存!");
        }
        //扣减库存，并返回剩余库存量
        long stock = redisTemplate.opsForValue().decrement(PRODUCT_FREFIX + id);
        if(stock < 0) {
            //标记本地缓存
            localCache.put(PRODUCT_FREFIX + id, true);
            //将redis自增回来
            redisTemplate.opsForValue().increment(PRODUCT_FREFIX + id);
            throw new SeckillException("Redis===>商品("+id+")无剩余库存!");
        }
        //更新数据库
        updateStock(id);
    }

    /**
     * 更新数据库的库存
     * @param id
     */
    private void updateStock(int id) {
        if(dao.decrStock(id) == 0) {
            throw new SeckillException("Mysql===>商品("+id+")无剩余库存!");
        }
        System.out.println("更新数据库...");
    }
}
